/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package cn.bbstone.pisces2.client;

import cn.bbstone.pisces2.client.base.ClientCmdRegister;
import cn.bbstone.pisces2.comm.Const;
import cn.bbstone.pisces2.config.ConfigFactory;
import cn.bbstone.pisces2.config.ConfigModel;
import cn.bbstone.pisces2.util.CtxUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Sends one message when a connection is open and echoes back any received
 * data to the server.  Simply put, the echo client initiates the ping-pong
 * traffic between the echo client and server by sending the first message to
 * the server.
 */
public final class FileClient {
    private static Logger log = LoggerFactory.getLogger(FileClient.class);

    // Configure the client.
    private EventLoopGroup group = new NioEventLoopGroup();

    public void startup() {
        try {
            // support multi-client
            ClientCmdRegister.init();
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            // ---- client outbound(BFileReq)

                            // ---- client inbound(BFileRsp)

                            p.addLast(new LengthFieldBasedFrameDecoder(12 * 1024 * 1024, 112, 4, 0, 0));
                            // decoder (NOTICE: will not work
//                            p.addLast(new RspListDataDecoder());
//                            p.addLast(new RspFileInfoDecoder());
//                            p.addLast(new RspFileDataDecoder());
                            // handler
                            p.addLast(new FileClientHandler());


                        }
                    });


            // Start the client.
            ChannelFuture f = b.connect(CtxUtil.getConfigModel().getServerHost(), CtxUtil.getConfigModel().getServerPort()).sync();

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("startup client error.", e);
        } finally {
            // Shut down the event loop to terminate all threads.
            group.shutdownGracefully();
        }
    }

    /**
     * shutdown event executors
     */
    public void shutdown() {
        log.info("client shutdown proceeding...");
        // shut down executor
        if (!group.isShutdown()) {
            group.shutdownGracefully();
        }
        // terminate all tasks
        if (!group.isTerminated()) {
            // both can be close by client api and auth fail
            // handler(AuthAnswerMessageHandler)
            Future<?> future = group.terminationFuture();
            future.addListener(new FutureListener<Object>() {
                @Override
                public void operationComplete(Future<Object> future) throws Exception {
                    if (future.isSuccess()) {
                        log.debug("****====== client EventLoopGroup terminate success. =======****");
                    }
                }
            });
        }
        log.info("event executor is shutdown.");

        try {
            // ensure that shutdown has actually completed and won't
            // cause class loader error if JVM starts unloading classes
            Thread.sleep(2);
        } catch (InterruptedException ignore) {
            // ignore
        }
        log.info("client shutdown success. ");
    }

}
